package cn.itsource.pethome.service.impl;

import cn.hutool.http.HttpUtil;
import cn.itsource.pethome.constant.Constant;
import cn.itsource.pethome.domain.Employee;
import cn.itsource.pethome.domain.User;
import cn.itsource.pethome.domain.WxUser;
import cn.itsource.pethome.domain.dto.TokenResult;
import cn.itsource.pethome.domain.dto.UserDto;
import cn.itsource.pethome.domain.dto.WxUserinfo;
import cn.itsource.pethome.mapper.EmployeeMapper;
import cn.itsource.pethome.mapper.UserMapper;
import cn.itsource.pethome.mapper.WxUserMapper;
import cn.itsource.pethome.query.UserQuery;
import cn.itsource.pethome.service.IUserService;
import cn.itsource.pethome.utils.AjaxResult;
import cn.itsource.pethome.utils.MD5Utils;
import cn.itsource.pethome.utils.RedisUtils;
import cn.itsource.pethome.utils.StrUtils;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Service
@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
public class UserServiceImpl extends BaseServiceImpl<User,UserQuery> implements IUserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private EmployeeMapper employeeMapper;
    @Autowired
    private WxUserMapper wxUserMapper;

    /**
     * 验证手机号
     * @param type phoneReg表示手机验证码注册 phoneLogin表示手机验证码登录
     * @param phone 手机号
     * @return
     */
    @Override
    public AjaxResult checkPhone(String type, String phone) {
        //通过手机号查询User
        User user = userMapper.findByPhone(phone);
        //手机验证码注册
        if("phoneReg".equals(type)){
            if(user == null){
                //该手机号可以注册
                return AjaxResult.me();
            }else{
                //该手机号已注册
                return AjaxResult.me().setSuccess(false).setMsg("该手机号已注册！");
            }
        }
        //手机验证码登录
        else if ("phoneLogin".equals(type)){
            if(user != null){
                //该手机号可以登录
                return AjaxResult.me();
            }else{
                //该手机号未注册
                return AjaxResult.me().setSuccess(false).setMsg("该手机号未注册！");
            }
        }
        return null;
    }

    /**
     * 发送手机验证码
     * @param type phoneReg表示手机验证码注册 phoneLogin表示手机验证码登录
     * @param phone 手机号
     * @return
     */
    @Override
    public AjaxResult sendMobileCode(String type, String phone) {
        //先从Redis中获取上次保存的验证码和时间数据
        String value = RedisUtils.INSTANCE.get(type + ":" + phone);
        //字符串非空，表示Redis中已有对应key的数据，也就是5分钟内多次获取验证码
        if(StringUtils.isNotBlank(value)){
            //拆分字符串
            String[] vals = value.split(":");
            //将上次获取验证码的时间毫秒字符串转化为long类型
            long lastTime = Long.valueOf(vals[1]);
            //本次获取验证码的时间毫秒
            long currentTime = System.currentTimeMillis();

            //1分钟内不能反复获取验证码
            if(currentTime - lastTime <= 60*1000){
                return AjaxResult.me().setSuccess(false).setMsg("1分钟内不能反复获取验证码！");
            }
            //5分钟内，再次获取验证码，与上次验证码相同
            if(currentTime - lastTime <= 300*1000){
                //2.重新保存一下
                RedisUtils.INSTANCE.set(type+":"+phone, vals[0]+":"+currentTime, 300);
                //3.调用短信平台接口，发送短信
                String content = "您正在注册宠物之家平台，验证码为：" + vals[0] + "，请在5分钟内完成操作！";
                System.out.println(content);
                //SmsUtils.INSTANCE.send(phone, content);
            }
        }else{
            //1.随机生成一个6位的数字验证码字符串
            String verifyCode = StrUtils.getRandomString(6);
            //2.保存到Redis中，设置过去时间5分钟   key的格式：type:手机号    value的格式：验证码:时间毫秒
            RedisUtils.INSTANCE.set(type+":"+phone, verifyCode+":"+System.currentTimeMillis(), 300);
            //3.调用短信平台接口，发送短信
            String content = "您正在注册宠物之家平台，验证码为：" + verifyCode + "，请在5分钟内完成操作！";
            System.out.println(content);
            //SmsUtils.INSTANCE.send(phone, content);
        }
        return AjaxResult.me();
    }

    /**
     * 用户注册
     * @param userDto
     */
    @Override
    @Transactional
    public AjaxResult userReg(UserDto userDto) {
        //1.先从Redis中获取验证码
        String value = RedisUtils.INSTANCE.get(userDto.getType() + ":" + userDto.getPhone());
        if(StringUtils.isNotBlank(value)){
            //拆分字符串
            String[] vals = value.split(":");
            //比较验证码
            if(vals[0].equals(userDto.getVerifyCode())){
                userDto.setState(Constant.NORMAL);
                userDto.setUsername(userDto.getPhone());
                userDto.setSalt(StrUtils.getComplexRandomString(32));   //设置盐值
                //设置加密后的密码
                userDto.setPassword(MD5Utils.encrypByMd5(userDto.getPassword(), userDto.getSalt()));
                //插入数据
                userMapper.save(userDto);
                return AjaxResult.me();
            }else{
                return AjaxResult.me().setSuccess(false).setMsg("验证码错误！");
            }
        }else{
            return AjaxResult.me().setSuccess(false).setMsg("验证码错误！");
        }
    }

    /**
     * 用户登录
     * userDto的type属性：
     *  front表示前台普通用户登录  查询t_user表
     *  admin表示后台管理系统用户登录  查询t_employee表
     * @param userDto
     * @return
     */
    @Override
    public AjaxResult login(UserDto userDto,HttpSession session) {
        if(Constant.FRONT_LOGIN.equals(userDto.getType())){
            //前台用户登录
            User user = userMapper.findByAccount(userDto.getUsername());
            if(user != null){
                //先将表单提交过来的密码进行加密后再与数据库查询到的密码进行比较
                String md5Password = MD5Utils.encrypByMd5(userDto.getPassword(), user.getSalt());
                if(user.getPassword().equals(md5Password)){
                    //保存当前用户
                    user.setPassword(null);
                    //随机生成一个UUID
                    String userToken = UUID.randomUUID().toString();
                    //将当前登录用户对象转化为JSON字符串
                    String loginUserJsonStr = JSON.toJSONString(user);
                    //保存到Redis中，30分钟过期【以uuid为key，以用户对象的json字符串为值】
                    RedisUtils.INSTANCE.set(userToken, loginUserJsonStr, Constant.EXPIRETIME_OF_LOGINUSER);

                    //一次性要返回多个数据，就封装一个Map集合或者实体类对象
                    Map<String, Object> map = new HashMap<>();
                    map.put("userToken", userToken);
                    map.put("loginUser", loginUserJsonStr);
                    return AjaxResult.me().setData(map);
                }
                return AjaxResult.me().setSuccess(false).setMsg("密码错误！");
            }
            return AjaxResult.me().setSuccess(false).setMsg("账户不存在！");
        }else if(Constant.ADMIN_LOGIN.equals(userDto.getType())){
            //后台用户登录
            Employee employee = employeeMapper.findByAccount(userDto.getUsername());
            if(employee != null){
                //先将表单提交过来的密码进行加密后再与数据库查询到的密码进行比较
                String md5Password = MD5Utils.encrypByMd5(userDto.getPassword(), "");
                if(employee.getPassword().equals(md5Password)){
                    //保存当前用户
                    employee.setPassword(null);
                    //随机生成一个UUID
                    String userToken = UUID.randomUUID().toString();
                    //将当前登录用户对象转化为JSON字符串
                    String loginUserJsonStr = JSON.toJSONString(employee);
                    //保存到Redis中，30分钟过期【以uuid为key，以用户对象的json字符串为值】
                    RedisUtils.INSTANCE.set(userToken, loginUserJsonStr, Constant.EXPIRETIME_OF_LOGINUSER);

                    //一次性要返回多个数据，就封装一个Map集合或者实体类对象
                    Map<String, Object> map = new HashMap<>();
                    map.put("userToken", userToken);
                    map.put("loginUser", loginUserJsonStr);
                    return AjaxResult.me().setData(map);
                }
                return AjaxResult.me().setSuccess(false).setMsg("密码错误！");
            }
            return AjaxResult.me().setSuccess(false).setMsg("账户不存在！");
        }
        return AjaxResult.me().setSuccess(false).setMsg("系统繁忙！");
    }

    /**
     * 微信登录：通过code获取AccessToken
     * @param code  微信APP中扫码成功后得到的code
     * @return
     */
    @Override
    public AjaxResult wechatLogin(String code) {
        //处理通过code获取AccessToken的接口地址
        String accessTokenUrl = Constant.ACCESS_TOKEN_URL
                .replace("APPID", Constant.APPID)
                .replace("SECRET", Constant.SECRET)
                .replace("CODE", code);
        //发送请求【hutool工具包】
        String resultStr = HttpUtil.get(accessTokenUrl);
        //将json字符串转化为实体类对象
        TokenResult tokenResult = JSON.parseObject(resultStr, TokenResult.class);
        System.out.println(tokenResult);
        //errcode为null时表示成功
        if(tokenResult.getErrcode() == null){
            //通过openid和unionid去查询t_wxuser表
            WxUser wxUser = wxUserMapper.findByOpenidAndUnionid(tokenResult.getOpenid(), tokenResult.getUnionid());
            if(wxUser != null){
                //表示该微信用户已绑定，也就是之前已经使用微信扫码登录过了：前端就直接跳转到首页
                User user = wxUser.getUser();
                //随机生成一个UUID
                String userToken = UUID.randomUUID().toString();
                //将当前登录用户对象转化为JSON字符串
                String loginUserJsonStr = JSON.toJSONString(user);
                //保存到Redis中，30分钟过期【以uuid为key，以用户对象的json字符串为值】
                RedisUtils.INSTANCE.set(userToken, loginUserJsonStr, Constant.EXPIRETIME_OF_LOGINUSER);

                //一次性要返回多个数据，就封装一个Map集合或者实体类对象
                Map<String, Object> map = new HashMap<>();
                map.put("userToken", userToken);
                map.put("loginUser", loginUserJsonStr);
                return AjaxResult.me().setMsg("binded").setData(map);
            }else{
                //未绑定，返回到前端，需要跳转到wechatUserBind.html页面
                return AjaxResult.me().setSuccess(false).setMsg("unbind").setData(tokenResult);
            }
        }
        return AjaxResult.me().setSuccess(false).setMsg("系统繁忙，请重试！" + tokenResult.getErrmsg());
    }

    /**
     * 微信用户绑定
     * @param userDto type等于phoneBind表示手机验证码绑定 usernameBind表示用户名密码绑定
     * @return
     */
    @Override
    @Transactional
    public AjaxResult wechatUserBind(UserDto userDto) {
        if("usernameBind".equals(userDto.getType())){
            //1.通过access_token和openid去微信服务器获取微信用户个人信息
            String wechat_userinfo_url = Constant.WECHAT_USERINFO_URL
                    .replace("ACCESSTOKEN", userDto.getAccess_token())
                    .replace("OPENID", userDto.getOpenid());
            //hutool工具包发请求
            String userinfoStr = HttpUtil.get(wechat_userinfo_url);

            //2.将json字符串转化为实体类对象
            WxUserinfo wxUserinfo = JSON.parseObject(userinfoStr, WxUserinfo.class);

            //3.封装一个User对象保存到t_user表，并且返回主键值
            userDto.setSalt(StrUtils.getComplexRandomString(32));       //盐值
            userDto.setState(Constant.NORMAL);      //状态为正常
            userDto.setPassword(MD5Utils.encrypByMd5(userDto.getPassword(), userDto.getSalt()));    //密码加密
            userDto.setHeadImg(wxUserinfo.getHeadimgurl());
            userMapper.save(userDto);

            //4.封装一个WxUser对象保存到t_wxuser表
            WxUser wxUser = new WxUser();
            wxUser.setOpenid(wxUserinfo.getOpenid());
            wxUser.setUnionid(wxUserinfo.getUnionid());
            wxUser.setHeadimgurl(wxUserinfo.getHeadimgurl());
            wxUser.setNickname(wxUserinfo.getNickname());
            wxUser.setSex(wxUserinfo.getSex());
            wxUser.setAddress(wxUserinfo.getCountry() + wxUserinfo.getProvince() + wxUserinfo.getCity());
            wxUser.setUser(userDto);
            wxUserMapper.save(wxUser);

            //5.返回前端
            //随机生成一个UUID
            String userToken = UUID.randomUUID().toString();
            userDto.setPassword(null);
            userDto.setAccess_token(null);
            userDto.setOpenid(null);
            userDto.setType(null);
            //将当前登录用户对象转化为JSON字符串
            String loginUserJsonStr = JSON.toJSONString(userDto);
            //保存到Redis中，30分钟过期【以uuid为key，以用户对象的json字符串为值】
            RedisUtils.INSTANCE.set(userToken, loginUserJsonStr, Constant.EXPIRETIME_OF_LOGINUSER);

            //一次性要返回多个数据，就封装一个Map集合或者实体类对象
            Map<String, Object> map = new HashMap<>();
            map.put("userToken", userToken);
            map.put("loginUser", loginUserJsonStr);
            return AjaxResult.me().setMsg("binded").setData(map);
        }else{

        }
        return AjaxResult.me().setSuccess(false).setMsg("微信用户绑定失败！");
    }


}
